1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package org.jcp.xml.dsig.internal.dom;
35
36 import javax.xml.crypto.*;
37 import javax.xml.crypto.dsig.*;
38 import javax.xml.crypto.dom.DOMCryptoContext;
39 import javax.xml.crypto.dom.DOMURIReference;
40
41 import java.io.*;
42 import java.net.URI;
43 import java.net.URISyntaxException;
44 import java.security.*;
45 import java.util.*;
46 import java.util.logging.Level;
47 import java.util.logging.Logger;
48 import org.w3c.dom.Attr;
49 import org.w3c.dom.Document;
50 import org.w3c.dom.Element;
51 import org.w3c.dom.Node;
52
53 import org.jcp.xml.dsig.internal.DigesterOutputStream;
54 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
55 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
56 import com.sun.org.apache.xml.internal.security.utils.Base64;
57 import com.sun.org.apache.xml.internal.security.utils.UnsyncBufferedOutputStream;
58
59
60
61
62
63
64
65 public final class DOMReference extends DOMStructure
66 implements Reference, DOMURIReference {
67
68
69
70
71
72
73
74
75 private static boolean useC14N11 =
76 AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
77 public Boolean run() {
78 return Boolean.getBoolean
79 ("com.sun.org.apache.xml.internal.security.useC14N11");
80 }
81 });
82
83 private static Logger log = Logger.getLogger("org.jcp.xml.dsig.internal.dom");
84
85 private final DigestMethod digestMethod;
86 private final String id;
87 private final List transforms;
88 private List allTransforms;
89 private final Data appliedTransformData;
90 private Attr here;
91 private final String uri;
92 private final String type;
93 private byte[] digestValue;
94 private byte[] calcDigestValue;
95 private Element refElem;
96 private boolean digested = false;
97 private boolean validated = false;
98 private boolean validationStatus;
99 private Data derefData;
100 private InputStream dis;
101 private MessageDigest md;
102 private Provider provider;
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119 public DOMReference(String uri, String type, DigestMethod dm,
120 List transforms, String id, Provider provider) {
121 this(uri, type, dm, null, null, transforms, id, null, provider);
122 }
123
124 public DOMReference(String uri, String type, DigestMethod dm,
125 List appliedTransforms, Data result, List transforms, String id,
126 Provider provider) {
127 this(uri, type, dm, appliedTransforms,
128 result, transforms, id, null, provider);
129 }
130
131 public DOMReference(String uri, String type, DigestMethod dm,
132 List appliedTransforms, Data result, List transforms, String id,
133 byte[] digestValue, Provider provider) {
134 if (dm == null) {
135 throw new NullPointerException("DigestMethod must be non-null");
136 }
137 this.allTransforms = new ArrayList();
138 if (appliedTransforms != null) {
139 List transformsCopy = new ArrayList(appliedTransforms);
140 for (int i = 0, size = transformsCopy.size(); i < size; i++) {
141 if (!(transformsCopy.get(i) instanceof Transform)) {
142 throw new ClassCastException
143 ("appliedTransforms["+i+"] is not a valid type");
144 }
145 }
146 this.allTransforms = transformsCopy;
147 }
148 if (transforms == null) {
149 this.transforms = Collections.EMPTY_LIST;
150 } else {
151 List transformsCopy = new ArrayList(transforms);
152 for (int i = 0, size = transformsCopy.size(); i < size; i++) {
153 if (!(transformsCopy.get(i) instanceof Transform)) {
154 throw new ClassCastException
155 ("transforms["+i+"] is not a valid type");
156 }
157 }
158 this.transforms = transformsCopy;
159 this.allTransforms.addAll(transformsCopy);
160 }
161 this.digestMethod = dm;
162 this.uri = uri;
163 if ((uri != null) && (!uri.equals(""))) {
164 try {
165 new URI(uri);
166 } catch (URISyntaxException e) {
167 throw new IllegalArgumentException(e.getMessage());
168 }
169 }
170 this.type = type;
171 this.id = id;
172 if (digestValue != null) {
173 this.digestValue = (byte[]) digestValue.clone();
174 this.digested = true;
175 }
176 this.appliedTransformData = result;
177 this.provider = provider;
178 }
179
180
181
182
183
184
185 public DOMReference(Element refElem, XMLCryptoContext context,
186 Provider provider) throws MarshalException {
187
188 Element nextSibling = DOMUtils.getFirstChildElement(refElem);
189 List transforms = new ArrayList(5);
190 if (nextSibling.getLocalName().equals("Transforms")) {
191 Element transformElem = DOMUtils.getFirstChildElement(nextSibling);
192 while (transformElem != null) {
193 transforms.add
194 (new DOMTransform(transformElem, context, provider));
195 transformElem = DOMUtils.getNextSiblingElement(transformElem);
196 }
197 nextSibling = DOMUtils.getNextSiblingElement(nextSibling);
198 }
199
200
201 Element dmElem = nextSibling;
202 this.digestMethod = DOMDigestMethod.unmarshal(dmElem);
203
204
205 try {
206 Element dvElem = DOMUtils.getNextSiblingElement(dmElem);
207 this.digestValue = Base64.decode(dvElem);
208 } catch (Base64DecodingException bde) {
209 throw new MarshalException(bde);
210 }
211
212
213 this.uri = DOMUtils.getAttributeValue(refElem, "URI");
214 this.id = DOMUtils.getAttributeValue(refElem, "Id");
215
216 this.type = DOMUtils.getAttributeValue(refElem, "Type");
217 this.here = refElem.getAttributeNodeNS(null, "URI");
218 this.refElem = refElem;
219 this.transforms = transforms;
220 this.allTransforms = transforms;
221 this.appliedTransformData = null;
222 this.provider = provider;
223 }
224
225 public DigestMethod getDigestMethod() {
226 return digestMethod;
227 }
228
229 public String getId() {
230 return id;
231 }
232
233 public String getURI() {
234 return uri;
235 }
236
237 public String getType() {
238 return type;
239 }
240
241 public List getTransforms() {
242 return Collections.unmodifiableList(allTransforms);
243 }
244
245 public byte[] getDigestValue() {
246 return (digestValue == null ? null : (byte[]) digestValue.clone());
247 }
248
249 public byte[] getCalculatedDigestValue() {
250 return (calcDigestValue == null ? null
251 : (byte[]) calcDigestValue.clone());
252 }
253
254 public void marshal(Node parent, String dsPrefix, DOMCryptoContext context)
255 throws MarshalException {
256 if (log.isLoggable(Level.FINE)) {
257 log.log(Level.FINE, "Marshalling Reference");
258 }
259 Document ownerDoc = DOMUtils.getOwnerDocument(parent);
260
261 refElem = DOMUtils.createElement
262 (ownerDoc, "Reference", XMLSignature.XMLNS, dsPrefix);
263
264
265 DOMUtils.setAttributeID(refElem, "Id", id);
266 DOMUtils.setAttribute(refElem, "URI", uri);
267 DOMUtils.setAttribute(refElem, "Type", type);
268
269
270 if (!allTransforms.isEmpty()) {
271 Element transformsElem = DOMUtils.createElement
272 (ownerDoc, "Transforms", XMLSignature.XMLNS, dsPrefix);
273 refElem.appendChild(transformsElem);
274 for (int i = 0, size = allTransforms.size(); i < size; i++) {
275 DOMStructure transform =
276 (DOMStructure) allTransforms.get(i);
277 transform.marshal(transformsElem, dsPrefix, context);
278 }
279 }
280
281
282 ((DOMDigestMethod) digestMethod).marshal(refElem, dsPrefix, context);
283
284
285 if (log.isLoggable(Level.FINE)) {
286 log.log(Level.FINE, "Adding digestValueElem");
287 }
288 Element digestValueElem = DOMUtils.createElement
289 (ownerDoc, "DigestValue", XMLSignature.XMLNS, dsPrefix);
290 if (digestValue != null) {
291 digestValueElem.appendChild
292 (ownerDoc.createTextNode(Base64.encode(digestValue)));
293 }
294 refElem.appendChild(digestValueElem);
295
296 parent.appendChild(refElem);
297 here = refElem.getAttributeNodeNS(null, "URI");
298 }
299
300 public void digest(XMLSignContext signContext)
301 throws XMLSignatureException {
302 Data data = null;
303 if (appliedTransformData == null) {
304 data = dereference(signContext);
305 } else {
306 data = appliedTransformData;
307 }
308 digestValue = transform(data, signContext);
309
310
311 String encodedDV = Base64.encode(digestValue);
312 if (log.isLoggable(Level.FINE)) {
313 log.log(Level.FINE, "Reference object uri = " + uri);
314 }
315 Element digestElem = DOMUtils.getLastChildElement(refElem);
316 if (digestElem == null) {
317 throw new XMLSignatureException("DigestValue element expected");
318 }
319 DOMUtils.removeAllChildren(digestElem);
320 digestElem.appendChild
321 (refElem.getOwnerDocument().createTextNode(encodedDV));
322
323 digested = true;
324 if (log.isLoggable(Level.FINE)) {
325 log.log(Level.FINE, "Reference digesting completed");
326 }
327 }
328
329 public boolean validate(XMLValidateContext validateContext)
330 throws XMLSignatureException {
331 if (validateContext == null) {
332 throw new NullPointerException("validateContext cannot be null");
333 }
334 if (validated) {
335 return validationStatus;
336 }
337 Data data = dereference(validateContext);
338 calcDigestValue = transform(data, validateContext);
339
340 if (log.isLoggable(Level.FINE)) {
341 log.log(Level.FINE, "Expected digest: "
342 + Base64.encode(digestValue));
343 log.log(Level.FINE, "Actual digest: "
344 + Base64.encode(calcDigestValue));
345 }
346
347 validationStatus = Arrays.equals(digestValue, calcDigestValue);
348 validated = true;
349 return validationStatus;
350 }
351
352 public Data getDereferencedData() {
353 return derefData;
354 }
355
356 public InputStream getDigestInputStream() {
357 return dis;
358 }
359
360 private Data dereference(XMLCryptoContext context)
361 throws XMLSignatureException {
362 Data data = null;
363
364
365 URIDereferencer deref = context.getURIDereferencer();
366 if (deref == null) {
367 deref = DOMURIDereferencer.INSTANCE;
368 }
369 try {
370 data = deref.dereference(this, context);
371 if (log.isLoggable(Level.FINE)) {
372 log.log(Level.FINE, "URIDereferencer class name: "
373 + deref.getClass().getName());
374 log.log(Level.FINE, "Data class name: "
375 + data.getClass().getName());
376 }
377 } catch (URIReferenceException ure) {
378 throw new XMLSignatureException(ure);
379 }
380
381 return data;
382 }
383
384 private byte[] transform(Data dereferencedData,
385 XMLCryptoContext context) throws XMLSignatureException {
386
387 if (md == null) {
388 try {
389 md = MessageDigest.getInstance
390 (((DOMDigestMethod) digestMethod).getMessageDigestAlgorithm());
391 } catch (NoSuchAlgorithmException nsae) {
392 throw new XMLSignatureException(nsae);
393 }
394 }
395 md.reset();
396 DigesterOutputStream dos;
397 Boolean cache = (Boolean)
398 context.getProperty("javax.xml.crypto.dsig.cacheReference");
399 if (cache != null && cache.booleanValue() == true) {
400 this.derefData = copyDerefData(dereferencedData);
401 dos = new DigesterOutputStream(md, true);
402 } else {
403 dos = new DigesterOutputStream(md);
404 }
405 OutputStream os = new UnsyncBufferedOutputStream(dos);
406 Data data = dereferencedData;
407 for (int i = 0, size = transforms.size(); i < size; i++) {
408 DOMTransform transform = (DOMTransform) transforms.get(i);
409 try {
410 if (i < size - 1) {
411 data = transform.transform(data, context);
412 } else {
413 data = transform.transform(data, context, os);
414 }
415 } catch (TransformException te) {
416 throw new XMLSignatureException(te);
417 }
418 }
419
420 try {
421 if (data != null) {
422 XMLSignatureInput xi;
423
424
425 boolean c14n11 = useC14N11;
426 String c14nalg = CanonicalizationMethod.INCLUSIVE;
427 if (context instanceof XMLSignContext) {
428 if (!c14n11) {
429 Boolean prop = (Boolean) context.getProperty
430 ("com.sun.org.apache.xml.internal.security.useC14N11");
431 c14n11 = (prop != null && prop.booleanValue() == true);
432 if (c14n11) {
433 c14nalg = "http://www.w3.org/2006/12/xml-c14n11";
434 }
435 } else {
436 c14nalg = "http://www.w3.org/2006/12/xml-c14n11";
437 }
438 }
439 if (data instanceof ApacheData) {
440 xi = ((ApacheData) data).getXMLSignatureInput();
441 } else if (data instanceof OctetStreamData) {
442 xi = new XMLSignatureInput
443 (((OctetStreamData)data).getOctetStream());
444 } else if (data instanceof NodeSetData) {
445 TransformService spi = null;
446 try {
447 spi = TransformService.getInstance(c14nalg, "DOM");
448 } catch (NoSuchAlgorithmException nsae) {
449 spi = TransformService.getInstance
450 (c14nalg, "DOM", provider);
451 }
452 data = spi.transform(data, context);
453 xi = new XMLSignatureInput
454 (((OctetStreamData)data).getOctetStream());
455 } else {
456 throw new XMLSignatureException("unrecognized Data type");
457 }
458 if (context instanceof XMLSignContext && c14n11
459 && !xi.isOctetStream() && !xi.isOutputStreamSet()) {
460 DOMTransform t = new DOMTransform
461 (TransformService.getInstance(c14nalg, "DOM"));
462 Element transformsElem = null;
463 String dsPrefix = DOMUtils.getSignaturePrefix(context);
464 if (allTransforms.isEmpty()) {
465 transformsElem = DOMUtils.createElement(
466 refElem.getOwnerDocument(),
467 "Transforms", XMLSignature.XMLNS, dsPrefix);
468 refElem.insertBefore(transformsElem,
469 DOMUtils.getFirstChildElement(refElem));
470 } else {
471 transformsElem = DOMUtils.getFirstChildElement(refElem);
472 }
473 t.marshal(transformsElem, dsPrefix, (DOMCryptoContext) context);
474 allTransforms.add(t);
475 xi.updateOutputStream(os, true);
476 } else {
477 xi.updateOutputStream(os);
478 }
479 }
480 os.flush();
481 if (cache != null && cache.booleanValue() == true) {
482 this.dis = dos.getInputStream();
483 }
484 return dos.getDigestValue();
485 } catch (Exception e) {
486 throw new XMLSignatureException(e);
487 }
488 }
489
490 public Node getHere() {
491 return here;
492 }
493
494 public boolean equals(Object o) {
495 if (this == o) {
496 return true;
497 }
498
499 if (!(o instanceof Reference)) {
500 return false;
501 }
502 Reference oref = (Reference) o;
503
504 boolean idsEqual = (id == null ? oref.getId() == null :
505 id.equals(oref.getId()));
506 boolean urisEqual = (uri == null ? oref.getURI() == null :
507 uri.equals(oref.getURI()));
508 boolean typesEqual = (type == null ? oref.getType() == null :
509 type.equals(oref.getType()));
510 boolean digestValuesEqual =
511 Arrays.equals(digestValue, oref.getDigestValue());
512
513 return (digestMethod.equals(oref.getDigestMethod()) && idsEqual &&
514 urisEqual && typesEqual && allTransforms.equals(oref.getTransforms()));
515 }
516
517 boolean isDigested() {
518 return digested;
519 }
520
521 private static Data copyDerefData(Data dereferencedData) {
522 if (dereferencedData instanceof ApacheData) {
523
524 ApacheData ad = (ApacheData) dereferencedData;
525 XMLSignatureInput xsi = ad.getXMLSignatureInput();
526 if (xsi.isNodeSet()) {
527 try {
528 final Set s = xsi.getNodeSet();
529 return new NodeSetData() {
530 public Iterator iterator() { return s.iterator(); }
531 };
532 } catch (Exception e) {
533
534 log.log(Level.WARNING,
535 "cannot cache dereferenced data: " + e);
536 return null;
537 }
538 } else if (xsi.isElement()) {
539 return new DOMSubTreeData
540 (xsi.getSubNode(), xsi.isExcludeComments());
541 } else if (xsi.isOctetStream() || xsi.isByteArray()) {
542 try {
543 return new OctetStreamData
544 (xsi.getOctetStream(), xsi.getSourceURI(), xsi.getMIMEType());
545 } catch (IOException ioe) {
546
547 log.log(Level.WARNING,
548 "cannot cache dereferenced data: " + ioe);
549 return null;
550 }
551 }
552 }
553 return dereferencedData;
554 }
555 }